/****************************************************************************
 *  _____       ______  _____
 * |_   _|     |  ____|/ ____|
 *   | |  _ __ | |__  | (___    Institute of Embedded Systems
 *   | | | '_ \|  __|  \___ \   Zürcher Hochschule für Angewandte
 *  _| |_| | | | |____ ____) |  Wissenschaften
 * |_____|_| |_|______|_____/   8401 Winterthur, Switzerland
 *
 ****************************************************************************
 *
 * @version $Rev: 146 $
 * @author  $Author: bacr $
 * @date    $Date: 2013-10-02 17:56:25 +0200 (mer., 02 oct. 2013) $
 *
 ***************************************************************************/

#ifndef __IP_PACKET_H__
#define __IP_PACKET_H__

#include "packet/net_address.h"

typedef struct ipv4_header_t    ipv4_header_t;
typedef struct ipv6_header_t    ipv6_header_t;
typedef struct ip_header_t      ip_header_t;
typedef struct ip_packet_t      ip_packet_t;

#define IP_PACKET(obj) ((ip_packet_t *) obj)

/* length on the wire! */
#define IPV4_PACKET_LEN                 20
#define IPV6_PACKET_LEN                 40

/* IPv4 offsets */
#define IPV4_PACKET_OFFSET_VERSION      0
#define IPV4_PACKET_OFFSET_TOS          1
#define IPV4_PACKET_OFFSET_LEN          2
#define IPV4_PACKET_OFFSET_ID           4
#define IPV4_PACKET_OFFSET_FLAGS        6
#define IPV4_PACKET_OFFSET_TTL          8
#define IPV4_PACKET_OFFSET_PROTOCOL     9
#define IPV4_PACKET_OFFSET_CHECKSUM     10
#define IPV4_PACKET_OFFSET_SRC          12
#define IPV4_PACKET_OFFSET_DEST         16

/* IPv6 offsets */
#define IPV6_PACKET_OFFSET_VER_FLOW     0
#define IPV6_PACKET_OFFSET_LEN          4
#define IPV6_PACKET_OFFSET_NEXT_HEADER  6
#define IPV6_PACKET_OFFSET_HOP_LIMIT    7
#define IPV6_PACKET_OFFSET_SRC          8
#define IPV6_PACKET_OFFSET_DEST         24

/* version */
#define IP_PACKET_OFFSET_VERSION        0
#define IP_PACKET_VERSION_V4            4
#define IP_PACKET_VERSION_V6            6

/* IPv4 header values */
#define IPV4_PACKET_IHL                 5           /* 5 x 32-bit = 160-bit = 20-byte */
#define IPV4_PACKET_MASK_DONT_FRAGMENT  0x4000
#define IPV4_PACKET_MASK_MORE_FRAGMENT  0x2000
#define IPV4_ADDRESS_BROADCAST          0xffffffff

/* IPv6 header values */
#define IPV6_PACKET_PRIORITY_MASK       0x0f

/* protocol */
#define IPV4_PROTOCOL_ICMP              1
#define IPV4_PROTOCOL_TCP               6
#define IPV4_PROTOCOL_UDP               17

#define IPV6_PROTOCOL_ICMP              58
#define IPV6_PROTOCOL_TCP               6
#define IPV6_PROTOCOL_UDP               17

/**
 * @struct ipv4_header_t
 * @brief  IP header
 * @see    ipv4_header_t
 *
 * @var    ipv4_header_t::ver_ihl
 * @brief  IP-Version and IP header length (IHL) in 32-bit units.
 *         ex. Value=5, 5 x 32-bit = 160-bit = 20-byte
 *
 * @var    ipv4_header_t::dscp
 * @brief  Differentiated Services Code Point (DSCP)
 *
 * @var    ipv4_header_t::len
 * @brief  Total length over IP header and payload
 *
 * @var    ipv4_header_t::id
 * @brief  Identification
 *
 * @var    ipv4_header_t::frag_offset
 * @brief  Fragment offset with flags
 *
 * @var    ipv4_header_t::ttl
 * @brief  Time to live
 *
 * @var    ipv4_header_t::protocol
 * @brief  Protocol (ex. TCP=6, UDP=17)
 *
 * @var    ipv4_header_t::checksum
 * @brief  Header checksum
 *
 * @var    ipv4_header_t::src
 * @brief  Source IP-address
 *
 * @var    ipv4_header_t::dest
 * @brief  Destination IP-address
 */
struct ipv4_header_t {
    union {
        uint8_t         ver_ihl;
        struct {
            uint8_t     version : 4;
            uint8_t     ihl     : 4;
        };
    };
    uint8_t             dscp;
    uint16_t            len;
    uint16_t            id;
    union {
        uint16_t        flags_offset;
        struct {
            uint16_t    flags  : 3;
            uint16_t    offset : 13;
        };
    };
    uint8_t             ttl;
    uint8_t             protocol;
    uint16_t            checksum;
    ipv4_address_t      src;
    ipv4_address_t      dest;
};

/**
 * @struct ipv6_header_t
 * @brief  IP header
 * @see    ipv6_header_t
 *
 * @var    ipv6_header_t::ver_prio
 * @brief
 *
 * @var    ipv6_header_t::flow_label
 * @brief
 *
 * @var    ipv6_header_t::len
 * @brief
 *
 * @var    ipv6_header_t::next_header
 * @brief
 *
 * @var    ipv6_header_t::hop_limit
 * @brief
 *
 * @var    ipv6_header_t::src
 * @brief
 *
 * @var    ipv6_header_t::dest
 * @brief
 */
struct ipv6_header_t {
    union {
        uint32_t        ver_flow;
        struct {
            uint32_t    flow_label : 24;
            uint32_t    priority   : 4;
            uint32_t    version    : 4;
        };
    };
    uint16_t            len;
    uint8_t             next_header;
    uint8_t             hop_limit;
    ipv6_address_t      src;
    ipv6_address_t      dest;
};

/* network byte order */
#define IP_HEADER                           \
    union {                                 \
        union {                             \
            uint8_t         ver_raw;        \
            struct {                        \
                uint8_t     unused  : 4;    \
                uint8_t     version : 4;    \
            };                              \
        };                                  \
        ipv4_header_t       ipv4;           \
        ipv6_header_t       ipv6;           \
    };

struct ip_header_t {
    IP_HEADER
};

/**
 * @struct ip_packet_t
 * @brief  IP packet
 * @see    ip_header_t
 *
 * @var    ip_packet_t::ether
 * @brief  Ethernet header
 */
struct ip_packet_t {
    object_t            obj;
    info_header_t       info;
    ethernet_header_t   ether;
    IP_HEADER
};

uint32_t    ip_packet_encode(ip_packet_t *this, raw_packet_t *raw_packet, uint32_t ip_offset);
void        ip_packet_decode(ip_packet_t *this, raw_packet_t *raw_packet, uint32_t ip_offset);

#endif

